import { CenoHeadersInit, Options } from "../types/options";

export const isObject = (value: unknown): value is object => value !== null && typeof value === 'object';

export const validateAndMerge = (...sources: Array<Partial<Options>| undefined>): Partial<Options> => {
  for (const source of sources) {
    if ((!isObject(source) || Array.isArray(source)) && source !== undefined) {
      throw new TypeError('The `options` argument must be an object')
    }
  }

  return deepMerge({}, ...sources)
}

export const mergeHeaders = (source1: CenoHeadersInit = {}, source2: CenoHeadersInit = {}) => {
  const response = new globalThis.Headers(source1 as RequestInit["headers"])
  const ok = source2 instanceof globalThis.Headers
  const source = new globalThis.Headers(source2 as RequestInit["headers"])

  for (const [key, value] of source.entries()) {
    if ((ok && value === 'undefined') || value === undefined) {
        response.delete(key)
    } else {
        response.set(key, value)
    }
  }

  return response
};

export const deepMerge = <T>(...sources: Array<Partial<T> | undefined>): T => {
	let returnValue: any = {};
	let headers = {};

	for (const source of sources) {
		if (Array.isArray(source)) {
			if (!Array.isArray(returnValue)) {
				returnValue = [];
			}

			returnValue = [...returnValue, ...source];
		} else if (isObject(source)) {
			for (let [key, value] of Object.entries(source)) {
				if (isObject(value) && key in returnValue) {
					value = deepMerge(returnValue[key], value);
				}

				returnValue = {...returnValue, [key]: value};
			}

			if (isObject((source as any).headers)) {
				headers = mergeHeaders(headers, (source as any).headers);
				returnValue.headers = headers;
			}
		}
	}

	return returnValue;
};